﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using CefSharp;
using CefSharp.Wpf;
using System.Windows.Forms.Integration;
using System.IO;
using System.Reflection;
using System.Windows.Input;
using V8.Net;
using DreamSpace.Server;
using DreamSpace.Net;
using DreamSpace.Net.ScriptEngines;

namespace DreamSpace.Studio
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        internal static readonly StudioHostBridge _ClientHostBridge = new StudioHostBridge();
        internal static readonly ServerHostBridge _ServerHostBridge = new ServerHostBridge();

        readonly List<string> _AttemptedLoadPaths = new List<string>();
        string _InitialWindowTitle;

        bool _IsConsoleVisible { get { return gridConsole.Visibility == Visibility.Visible; } }

        public static MainWindow Current { get { return (MainWindow)Application.Current.MainWindow; } }

        public static void AddConsoleLine(string line)
        {
            // ... a message is available, so make sure the console output pane is visible to display the message (most probably an error) ...
            if (!Current._IsConsoleVisible)
                Current.MenuItem_View_ShowOrHideConsolePane(null, null);

            Current.tbConsole.AppendText(Environment.NewLine + line);
        }

        public MainWindow()
        {
            InitializeComponent();

            _InitialWindowTitle = Title;

            MenuItem_View_ShowOrHideConsolePane(null, null); // (hides the console for now)

            App.WebViewer.ConsoleMessage += _WebViewer_ConsoleMessage;
            App.WebViewer.Loaded += (_s, _e) =>
            {
                Dispatcher.BeginInvoke((Action)(() => { _WebViewer_Loaded(_s, _e); }));
                // (this lets the viewer complete the loading process before using the control to prevent any possible initialization issues)
            };
            App.WebViewer.LoadCompleted += _WebViewer_LoadCompleted;

            gridWebViewer.Children.Add(App.WebViewer);

            KeyDown += MainWindow_KeyDown;

            App.ServerHost.ScriptEngine.DefineGlobalProperty("host", _ServerHostBridge, ScriptMemberSecurity.Locked);
            App.WebViewer.RegisterJsObject("host", _ClientHostBridge);

        }

        void _WebViewer_LoadCompleted(object sender, LoadCompletedEventArgs url)
        {
            StudioHostBridge.UIInvoke(() =>
            {
                if (Title == _InitialWindowTitle)
                    Title = App.Title;
            });

            if (!App.StartupFile.IsNullOrWhiteSpace())
                try
                {
                    App.WebViewer.ExecuteScript(File.ReadAllText(App.StartupFile));
                }
                catch (Exception ex)
                {
                    AddConsoleLine("Failed to load the startup file '" + App.StartupFile + "' into the viewer (CEF): " + Exceptions.GetFullErrorMessage(ex));
                    return;
                }
        }

        void _WebViewer_ConsoleMessage(object sender, ConsoleMessageEventArgs e)
        {
            StudioHostBridge.UIInvoke(() =>
            {
                AddConsoleLine(e.Message + Environment.NewLine + "in " + e.Source + " at line " + e.Line + ".");
            });
        }

        void MainWindow_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.F12)
                App.WebViewer.ShowDevTools();
        }

        void _WebViewer_Loaded(object sender, RoutedEventArgs e)
        {
            // ... the viewer is ready, so now the server libraries are loaded first ...

            try
            {
                App.LoadServerJSLibraries(); // (note: server only! [the web viewer will loaded an html page])
            }
            catch (Exception ex)
            {
                AddConsoleLine(Exceptions.GetFullErrorMessage(ex));
                return;
            }

            // ... now that the server is ready, the same libraries are needed in the viewer for proper interoperability - this is done via "default.html" ...
            // (note: this is why application ".js" files need to wait for the onReady event)

            if (!TryLoadDefaultHTML(Directory.GetCurrentDirectory())
                && !TryLoadDefaultHTML(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)))
            {
                Title = App.Title;
                MessageBox.Show("Locations searched:\r\n" + string.Join("\r\n", _AttemptedLoadPaths.ToArray()),
                    "The 'default.htm' file is missing.");
                Close();
                return;
            }
        }

        private bool TryLoadDefaultHTML(string path)
        {
            if (!path.EndsWith(Path.DirectorySeparatorChar.ToString()))
                path += Path.DirectorySeparatorChar;

            var pathURL = "file:///" + path.Replace('\\', '/').Replace(" ", "%20");

            _AttemptedLoadPaths.Add(pathURL);

            if (File.Exists(path + @"default.html"))
                App.WebViewer.Load(pathURL + "default.html");
            else if (File.Exists(path + @"default.htm"))
                App.WebViewer.Load(pathURL + "default.htm");
            else
            {
                path += @"\JSApp\";
                pathURL += "JSApp/";

                _AttemptedLoadPaths.Add(pathURL);

                if (File.Exists(path + @"default.html"))
                    App.WebViewer.Load(pathURL + "default.html");
                else if (File.Exists(path + @"default.htm"))
                    App.WebViewer.Load(pathURL + "default.htm");
                else
                    return false;
            }

            return true;
        }

        private void MenuItem_File_Exit(object sender, RoutedEventArgs e)
        {
            Close();
        }

        private void MenuItem_View_DevTools(object sender, RoutedEventArgs e)
        {
            App.WebViewer.ShowDevTools();
        }

        private void MenuItem_View_ShowOrHideConsolePane(object sender, RoutedEventArgs e)
        {
            if (_IsConsoleVisible)
            {
                gsConsole.Visibility = Visibility.Collapsed;
                gridConsole.Visibility = Visibility.Collapsed;
            }
            else
            {
                gsConsole.Visibility = Visibility.Visible;
                gridConsole.Visibility = Visibility.Visible;
            }
        }

        private void tbConsole_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            MenuItem_View_DevTools(sender, e);
        }

        private void MenuItem_Debug_Run(object sender, RoutedEventArgs e)
        {
            //if ((bool)App._WebViewer.EvaluateScript("typeof $DS.run != 'undefined'"))
            //    App._WebViewer.ExecuteScript("$DS.run();");
            //else
            //    MessageBox.Show("The system is already running.", "Run DreamSpaceJS ...");

            IBasicHandle result = App.ServerHost.Execute("typeof $DS.run != 'undefined'");

            if (result.IsError)
                MessageBox.Show("An error has occurred - the script environment may not have loaded correctly.");
            else if (result.AsBoolean)
            {
                result = App.ServerHost.Execute("$DS.run();", App.Title, false);
                if (result.IsError)
                    AddConsoleLine(result.AsString);
                App.WebViewer.ExecuteScript("$DS.run();");
            }
            else
                MessageBox.Show("The system is already running.", "Run DreamSpaceJS ...");
        }

        private void MenuItem_View_Refresh(object sender, RoutedEventArgs e)
        {
            App.WebViewer.Reload(true);
            tbConsole.Text = "";
        }
    }
}
